package beerAgent;

import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;

import beerAgent.FallingObject.Direction;

public class BeerGame implements BeerModel {
	private List<FallingObject> objects = new ArrayList<FallingObject>();

	private final static int mapHeight = 15;
	private final static int mapWidth = 30;
	private final static int playerLength = 5;
	private static final double captureRate = 0.8;
	private int maxObjectSize, minObjectSize;
	private int playerColumn=10;
	private FallingObject fallingObject;

	private BeerPanel panel;
	private int count = 0;
	private boolean draw = true;
	private boolean horizontalSpeed;


	
	public BeerGame(int minLength, int maxLength, boolean horizontalSpeed) {
		super();
		panel = new BeerPanel(this);
		this.horizontalSpeed = horizontalSpeed; 
		this.maxObjectSize = maxLength;
		this.minObjectSize = minLength;
	}

	public char getCell(int row,int col){
		int objectRow = fallingObject.getRow();
		
		if(row == mapHeight-1 && containsPlayer(col)) {
			return 'p';
		}
		else if(row == objectRow && containsObject(col)) {
			return 'o';
		}
		return '.';
	}

	public boolean[] getSensors() {
		boolean[] sensors = new boolean[playerLength];
//		Arrays.fill(sensors, Boolean.FALSE);
		int sensor = 0;
		int modi;
		
		for (int i = playerColumn; i < playerColumn+playerLength; i++) {
			modi = modulus(i, mapWidth);
			if (containsObject(modi)) {
				sensors[sensor] = true;
			}
			sensor++;
		}
		if (fallingObject == null || fallingObject.getRow() < 0) {
			return sensors; 
		}
		return sensors;
	}
	
	private boolean containsObject (int column) {
		if (fallingObject == null) return false;
		for (int i = fallingObject.getColumn(); i < fallingObject.getColumn()+fallingObject.getLength(); i++) {
			int modi = modulus(i, mapWidth);
			if (modi == column) return true;
		}
		return false;
	}
	
	private boolean containsPlayer (int column) {
		for (int i = playerColumn; i < playerColumn+playerLength; i++) {
			int modi = modulus(i, mapWidth);
			if (modi == column) return true;
		}
		return false;
	}

	public void reset() {
		if (fallingObject != null) fallingObject.setRow(-2);
		playerColumn = 10;
		count = 0;
		for (FallingObject object : objects) {
			object.reset(); 
		}
	}
	
	public BeerGame copy(){
		BeerGame copy = new BeerGame(minObjectSize, maxObjectSize, this.horizontalSpeed);
		copy.objects = new ArrayList<FallingObject>();
		for (int i = 0; i < this.objects.size(); i++) {
			copy.objects.add(this.objects.get(i).copy()); 
		}
		
		copy.reset(); 
		return copy; 
	}

	public Scenario move(Action action, int steps) {
		fallingObject = getFallingObject(count);
		
		Scenario returnValue = Scenario.NEUTRAL; 

		switch (action) {
		case MOVE_LEFT:
			playerColumn = moveLeft(steps);
			returnValue = dropFallingObject(fallingObject);
			break; 
		case MOVE_RIGHT:
			playerColumn = moveRight(steps);
			returnValue = dropFallingObject(fallingObject);
			break; 
		default:
			returnValue = dropFallingObject(fallingObject);
			break; 
		}
		if (draw){
			panel.drawGame();
		}
		return returnValue; 
	}

	private FallingObject getFallingObject(int count) {
		if (objects.size() == 0) objects.add(new FallingObject(maxObjectSize, minObjectSize, mapWidth, horizontalSpeed));

		return objects.get(count);
	}

	private Scenario dropFallingObject(FallingObject fallingObject) {
		fallingObject.setRow(fallingObject.getRow()+1);
		if(fallingObject.getDirection() == Direction.LEFT) fallingObject.setColumn(modulus(fallingObject.getColumn()-1, mapWidth));
		else if(fallingObject.getDirection() == Direction.RIGHT) fallingObject.setColumn(modulus(fallingObject.getColumn()+1, mapWidth));
		
		int objectLength = fallingObject.getLength();
		int objectRow = fallingObject.getRow();
		
		if (objectRow == mapHeight-1)fallingObject.reset();
		if(objectRow == mapHeight-2) {
			count += 1;
			objects.add(new FallingObject(maxObjectSize, minObjectSize, mapWidth, horizontalSpeed));
//			fallingObject.setRow(-1);
			
			if (objectLength >= playerLength && sensorsTrue() == playerLength) return Scenario.CATCH_LARGE;
			else if (sensorsTrue() > objectLength*getCaptureRate()) return Scenario.CATCH_SMALL;
			else if ((sensorsTrue() == 0) && (objectLength >= playerLength)) return Scenario.MISS_LARGE;
			else if ((sensorsTrue() < playerLength) && (objectLength >= playerLength)) return Scenario.HIT_BY_LARGE;
			else return Scenario.MISS_SMALL;
				
			
		}
		
		return Scenario.NEUTRAL;
	}

	private int sensorsTrue() {
		int sum = 0;
		boolean[] sensors = getSensors();
		for (boolean sensor : sensors) {
			if (sensor) sum += 1;
		}
		return sum;
	}

	private static int modulus(int a, int b){
		while (a < 0) a += b;
		return a%b;
	}

	private int getPlayerColumn() {
		return playerColumn;
	}

	private double getCaptureRate() {
		return captureRate;
	}

	private int moveLeft(int steps) {
		return modulus(getPlayerColumn()-steps, mapWidth);
	}

	private int moveRight(int steps) {
		return modulus(getPlayerColumn()+steps, mapWidth);
	}

	public JPanel getGamePanel() {
		if (panel == null){
			return new JPanel(); 
		}
		return panel;
	}

	public void setDraw(boolean draw){
		this.draw = draw; 
		if (draw){
			panel = new BeerPanel(this);
		}
	}

	public int getMapHeight() {
		return mapHeight;
	}

	public int getMapWidth() {
		return mapWidth;
	}

	public int getMapDim() {
		return mapWidth*mapHeight;
	}
	
	@Override
	public String toString() {
		String map = "";
		for (int i = 0; i < mapHeight; i++) {
			for (int j = 0; j < mapWidth; j++) {
				map += getCell(i, j);
			}
			map += "\n";
		}
		return map;
	}	
}
